﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.Reflection.Emit;
using System.Reflection;
using System.Linq;

namespace Mesuro.Common {
	public enum FilterBuilderComparison { Equal, NotEqual, Less, Greater, LessEqual, GreaterEqual }
	public class FilterBuilder {
		private static readonly Type[] predicateParameters = { typeof( Vector ) };
		private static readonly MethodInfo VectorIndexAccessor =
			typeof( Vector ).GetProperties( BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance )
			.First( p => p.GetIndexParameters().GetLength( 0 ) == 1 ).GetGetMethod();

		private readonly ObservableCollection<FilterBuilderElement> _list = new ObservableCollection<FilterBuilderElement>();
		public ObservableCollection<FilterBuilderElement> List {
			get { return _list; }
		}

		private SeriesFilter filter = new SeriesFilter();

		public SeriesFilter Compile() {
			DynamicMethod predicate = new DynamicMethod( "", typeof( bool ), predicateParameters );
			ILGenerator il = predicate.GetILGenerator();

			Label retFalse = il.DefineLabel();

			for (int i = 0; i < List.Count; i++) {
				FilterBuilderElement el = List[i];
				if (!el.Active) continue;
				il.Emit( OpCodes.Ldarga, 0 ); //Load the vector [1]
				il.Emit( OpCodes.Ldc_I4, (Int32)el.ChannelIndex );//Load index of comparison channel [2]
				il.Emit( OpCodes.Call, VectorIndexAccessor );//get comparison channel value [1]
				il.Emit( OpCodes.Ldc_R8, (double)el.Value );//Load comparison constant value [2]

				switch (el.Comparison) {
					case FilterBuilderComparison.Equal:
						il.Emit( OpCodes.Ceq );//[1]
						il.Emit( OpCodes.Brfalse, retFalse );//[0]
						break;
					case FilterBuilderComparison.NotEqual:
						il.Emit( OpCodes.Ceq );//[1]
						il.Emit( OpCodes.Brtrue, retFalse );//[0]
						break;
					case FilterBuilderComparison.Less:
						il.Emit( OpCodes.Clt );//[1]
						il.Emit( OpCodes.Brfalse, retFalse );//[0]
						break;
					case FilterBuilderComparison.Greater:
						il.Emit( OpCodes.Cgt );//[1]
						il.Emit( OpCodes.Brfalse, retFalse );//[0]
						break;
					case FilterBuilderComparison.LessEqual:
						il.Emit( OpCodes.Cgt );//[1]
						il.Emit( OpCodes.Brtrue, retFalse );//[0]
						break;
					case FilterBuilderComparison.GreaterEqual:
						il.Emit( OpCodes.Clt );//[1]
						il.Emit( OpCodes.Brtrue, retFalse );//[0]
						break;
				}
			}
			//Return true:
			il.Emit( OpCodes.Ldc_I4_1 );//[1]
			il.Emit( OpCodes.Ret );//[0]

			//Return false:
			il.MarkLabel( retFalse );
			il.Emit( OpCodes.Ldc_I4, 0 );//[1]
			il.Emit( OpCodes.Ret );//[0]

			filter.Predicate = (Predicate<Vector>)predicate.CreateDelegate( typeof( Predicate<Vector> ) );
			return filter;
		}
	}
	public class FilterBuilderElement {
		public FilterBuilderComparison Comparison { get; set; }
		public IChannelRef ChannelRef { get; set; }
		public int? ChannelIndex {
			get {
				return DataSet.GetSeries( ChannelRef ).Channels.IndexOf( ChannelRef.Channel );
			}
		}
		public double Value { get; set; }

		private bool _active = true;
		public bool Active {
			get { return _active; }
			set { _active = value; }
		}
	}
}
